#!/usr/bin/perl

# $Id: storage_server.pl 20 2010-11-12 10:03:27Z dolbanavt@googlemail.com $
# $Date: 2010-11-12 12:03:42 +0200 (Птн, 12 Ноя 2010) $
# $HeadURL: https://tegusigalpa.googlecode.com/svn/branches/storage_init/storage_server.pl $
# $LastChangedRevision: 65 $

use lib::abs qw( base_lib lib );
use JIP::ToolSet;
use Carp qw( croak );
use Const::Fast qw( const );
use English qw( -no_match_vars );
use Pod::Usage qw( pod2usage );
use Getopt::Long 2.24 qw( :config no_auto_abbrev no_ignore_case );
use File::Spec::Functions qw( catfile );
use FindBin qw( $RealBin );
use JIP::Utils qw( get_log_name );
use Proc::Lock;
use Configurator;
use Log;
use Proc::Daemon;
use JSON::RPC::Server::Daemon;
use SQLWrapper;
use Object::Container 'container';
use Tega::DBWrapper;

our $VERSION = '0.0.1';

# more meaningfull names for pod2usage's -verbose parameter
const my $SHOW_USAGE_ONLY         => 0;
const my $SHOW_BRIEF_HELP_MESSAGE => 1;
const my $SHOW_FULL_MANUAL        => 2;

# more meaningfull names for pod2usage's -exitval parameter
const my $RETURN_SUCCESS     => 0;
const my $RETURN_FATAL_ERROR => 1;
const my $RETURN_BAD_OPTIONS => 2;

#-- ui -------------------------------------------------------------------------
my $options_okay = GetOptions(
    'help'   => \my $is_help_requested,
    'conf=s' => \my $path_to_conf_file,
);

# show appropriate help message
pod2usage( -exitval => $RETURN_BAD_OPTIONS, -verbose => $SHOW_USAGE_ONLY )
    unless $options_okay;

pod2usage( -exitval => $RETURN_SUCCESS, -verbose => $SHOW_FULL_MANUAL )
    if $is_help_requested;

# check required options
pod2usage(
    -exitval => $RETURN_BAD_OPTIONS,
    -message => 'required parameter "conf" not defined',
    -verbose => $SHOW_USAGE_ONLY,
) unless $path_to_conf_file;

#-- init settings --------------------------------------------------------------
container->register( 'conf', sub {
    return Configurator->new( conf => $path_to_conf_file )->get_settings;
} );

#-- init log -------------------------------------------------------------------
container->register( 'log', sub {
    return Log->new(
        log_file  => catfile( container('conf')->log_dir, get_log_name() ),
        log_level => container('conf')->log_level,
    );
} );

#-- signal handlers ------------------------------------------------------------
require( catfile( $RealBin, 'signal_handlers.pl' ) );

exists $SIG{'INT'} ? ( $SIG{'INT'} = \&int_signal_handler )
    : container('log')->Warn('SIGINT signal is not handled properly');

exists $SIG{'TERM'} ? ( $SIG{'TERM'} = \&term_signal_handler )
    : container('log')->Warn('SIGTERM signal is not handled properly');

#-- check lock -----------------------------------------------------------------
container('log')->Note('setup lock');

my $process = Proc::Lock->new( lock_file => container('conf')->lock_file );

$process->lock();

#-- daemon ---------------------------------------------------------------------
container('log')->Note('demonize...');

Proc::Daemon->new( detach => container('conf')->should_detach )->demonize();

#-- db wrapper -----------------------------------------------------------------
container->register( 'db_wrapper', sub {
    return Tega::DBWrapper->new( connection => SQLWrapper->new(
        dsn  => container('conf')->db->dsn,
        user => container('conf')->db->user,
        pass => container('conf')->db->password,
    ) );
} );

#-- spawn server ---------------------------------------------------------------
container('log')->Debug( 'local port: '. container('conf')->local_port );

container('log')->Note('fire started RPC daemon');

JSON::RPC::Server::Daemon->new( LocalPort => container('conf')->local_port )
                         ->dispatch('Tega::StorageAPI')
                         ->handle();

#-- finalyze -------------------------------------------------------------------
container('log')->Note('unlock session...')
    and $process->unlock()
    and container('log')->Note('exiting');

#-- subroutines ----------------------------------------------------------------
# код JSON::RPC::Server::Daemon перехватывает _эти_ сигналы и прерывает
# свой рабочий цикл
sub int_signal_handler {
    container('log')->Note('received INT signal...terminating...');
}

sub term_signal_handler {
    container('log')->Note('received TERM signal...terminating...');
}

#-- end ------------------------------------------------------------------------

__END__

=head1 NAME

storage_server.pl - стартер JSON-RPC демона

=head1 SYNOPSIS

storage_server.pl [options]

 Options:
   --help            show full help message
   --conf            path to config file

=head1 USAGE

получение справочной информации:
/path/to/storage_server.pl --help

активация режима RPC-сервера:
/path/to/storage_server.pl --conf=/path/to/my_conf.pm

=head1 DESCRIPTION

Программа предоставляет среду для выполнения процесса RPC-сервера.
Управляется через интерфейс командной строки.

=head1 DEPENDENCIES

lib::abs (from CPAN)

Const::Fast (from CPAN)

JSON::RPC::Server::Daemon (from CPAN)

Object::Container (from CPAN)

JIP::ToolSet (from base_lib)

Proc::Lock (from base_lib)

Configurator (from base_lib)

Log (from base_lib)

Proc::Daemon (from base_lib)

JIP::Utils (from base_lib)

SQLWrapper (from base_lib)

=head1 EXIT STATUS

0 - программа выполнена успешно

1 - критическая ошибка программы

2 - неверные параметры командной строки

=head1 AUTHOR

Vladimir Zhavoronkov, <dolbanavt (at) Google Mail>

http://dolbanavt.livejournal.com/

=head1 LICENSE AND COPYRIGHT

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

=cut
